#ifndef __BLOCKRENDERERS__
#define __BLOCKRENDERERS__

#include <map>
#include <vector>
#include <sstream>
#include <exception>
#include <wx\string.h>
#include <wx\wfstream.h>
#include <wx\archive.h>
#include <wx\fs_arc.h>
#include <wx\zipstrm.h>
#include <wx\fs_zip.h>

class Block;
class ITexture;
class IBlockRenderer;
class RenderScriptEngine;
class asIScriptModule;
class RendererBase;
class Schematic;
class Block;
class PrimitiveRendererBase;
class BlockRenderers;

#include "..\Math\Point3.h"
#include "Fonts.h"
#include "..\Schematics\BlockNames.h"

class ZipFile
{
	wxFileInputStream *_fileStream;
	wxZipInputStream *_stream;
	std::map<wxString, std::auto_ptr<wxZipEntry>> _entries;

public:
	ZipFile();
	~ZipFile();
	wxInputStream &GetStream();
	bool IsSet();
	bool Exists(const wxString &file);
	void Open(const wxString &file);
	wxFileOffset OpenEntry(const wxString &file);
	void CloseEntry();
	void Close();
};

class TexturePackHandler
{
	BlockRenderers *_blocks;
	RendererBase *_base;
	ZipFile _tp, _default;

public:
	static inline const std::string &_getDefaultPack()
	{
		static bool _created = false;
		static std::string _defaultPack;

		if (!_created)
		{
			std::stringstream str;
			str << getenv("appdata") << "\\.minecraft\\bin\\minecraft.jar";
			_defaultPack = str.str();
			_created = true;
		}
		
		return _defaultPack;
	}

	TexturePackHandler(BlockRenderers *blocks, const wxString &tp, RendererBase *renderer);
	~TexturePackHandler();

	void SetPack(const wxString &string);
	void CreateScaledTexture(std::pair<wxString, ITexture*> &texture);
	ITexture *CreateScaledTexture(const wxString &str);
	std::shared_ptr<byte> LoadFile(const wxString &str);
};

const int BLOCK_ISFULL = 1;
const int BLOCK_ISOPAQUE = 2;

struct BlockRendererData
{
	asIScriptModule		*Module;
	void				*Data;
	byte				Flags;

	BlockRendererData(asIScriptModule *module, void *data, const bool &isFull, const bool &isOpaque) :
		Module(module),
		Data(data),
		Flags(0)
	{
		if (isFull)
			Flags |= BLOCK_ISFULL;

		if (isOpaque)
			Flags |= BLOCK_ISOPAQUE;
	}

	inline bool IsFull() { return (Flags & BLOCK_ISFULL) != 0; }
	inline bool IsOpaque() { return (Flags & BLOCK_ISOPAQUE) != 0; }
};

/// <summary>
/// Class to manage block renderers.
/// </summary>
class BlockRenderers
{
	friend class TexturePackHandler;

	std::map<wxString, ITexture*> _texturePool;
	TexturePackHandler *_texturePack;
	RenderScriptEngine *_scriptEngine;
	ITexture *_unknownTex;
	Fonts _fonts;
	BlockNames _names;

public:
	inline TexturePackHandler *GetTexturePack() { return _texturePack; }
	inline RenderScriptEngine *GetScriptEngine() { return _scriptEngine; }

	std::map<int, BlockRendererData> Renderers;

	BlockRenderers(RendererBase *renderer, const std::string &texturePack);
	~BlockRenderers();

	ITexture *GetTexture(const wxString &string);
	void AddTexture(const wxString &string, ITexture *texture);
	void SetTexturePack(const wxString &string);

	void AddRender(const int &id, void *renderer);
	void ExecuteRenderer(Schematic *schematic, PrimitiveRendererBase *renderer, const Block &block, const Point3i &position);
	
	bool IsFullBlock(const Block &block);
	bool IsOpaqueBlock(const Block &block);

	inline Fonts *GetFonts() { return &_fonts; }
	inline const std::string &GetBlockName(const int &id, const int &data) { return _names.Get(id, data); }
};

#endif